
package com.x52im.rbchat.permission2;

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.util.Log;

import com.eva.android.widget.WidgetUtils;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.x52im.rbchat.R;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Observer;

import androidx.annotation.NonNull;

/**
 * 用于支持最新Android 13 及以上版本的动态权限管理器。
 *
 * @author Jack Jiang
 * @since 7.2
 */
public class PermissionManager
{
    private final static String TAG = PermissionManager.class.getSimpleName();

    /**
     * 请求 位置相关 权限（此种组合，通常用于视频聊天、短视频录制时）。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     */
    public static void requestPermission_LOCATION(final Activity activity
            , final Observer obsOnGranted, Observer obsOnDenied)
    {
        // 本次要动态申请的权限
        final String[] pemmisions = {Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION};

        // 如果拒绝或没有获取到权限时的观察者没有设置，则给个默认的（主要用于信息提示）
        if(obsOnDenied ==null)
        {
            obsOnDenied = (o, permissionNamesObj) -> {
                final List<String> permissionNames = (List<String>)permissionNamesObj;
                String message = MessageFormat.format(activity.getResources().getString(R.string.rb_permission_fail_to_cancel), permissionNames);
                Log.w(TAG, "[动态权限onDenied]"+message);
                WidgetUtils.showToastLong(activity, message, WidgetUtils.ToastType.WARN);
            };
        }

        // 开始动态权限申请（用于targetSDKVersion>=23，即Android6.0及以上系统时）
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , true
                , pemmisions);
    }

    /**
     * 请求 POST_NOTIFICATIONS 权限。
     * 注：Android 13开始，新增了通知权限，详情参见：https://mp.weixin.qq.com/s/BQ57pzaffB8vPyGuJtc37w 。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     * @since 9.0
     */
    public static void requestPermission_POST_NOTIFICATIONS(final Context activity
            , final Observer obsOnGranted, Observer obsOnDenied)
    {
        // 本次要动态申请的权限
        final String[] pemmisions = {Permission.POST_NOTIFICATIONS};

        // 如果拒绝或没有获取到权限时的观察者没有设置，则给个默认的（主要用于信息提示）
        if(obsOnDenied == null)
        {
            obsOnDenied = (o, permissionNamesObj) -> {
                final List<String> permissionNames = (List<String>)permissionNamesObj;
                String message = MessageFormat.format(activity.getResources().getString(R.string.rb_permission_fail_to_cancel), permissionNames);
                Log.w(TAG, "[动态权限onDenied]"+message);
                WidgetUtils.showToastLong(activity, message, WidgetUtils.ToastType.WARN);
            };
        }

        // 开始动态权限申请（用于targetSDKVersion>=23，即Android6.0及以上系统时）
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , true
                , pemmisions);
    }

    /**
     * 请求 存储相关 权限。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     * @param showSettingIfNever true表示当正在获取已被用户永久拒绝的权限时自动跳到系统权限设置界面，否则不跳转
     */
    public static void requestPermission_STORAGE(final Activity activity
            , final Observer obsOnGranted, final Observer obsOnDenied, final boolean showSettingIfNever)
    {
        // 本次要动态申请的权限（在Android13及以上版本中，使用READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE无法实现权限申请）
//      final String[] pemmisions = {Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE};
        final String[] pemmisions = {Permission.READ_MEDIA_IMAGES, Permission.READ_MEDIA_VIDEO, Permission.READ_MEDIA_AUDIO, Permission.READ_MEDIA_VISUAL_USER_SELECTED};
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , showSettingIfNever
                , pemmisions);
    }

    /**
     * 请求 存储相关、通知相关 权限。
     * <p>
     * 本方法相当于 requestPermission_POST_NOTIFICATIONS、requestPermission_STORAGE 两个方法的合体。
     * <p>
     * 本方法目前是在APP启动时被调用，目的是确保本方法中的权限的APP实质性运行前尽可能早地把权限申请好，否则APP
     * 启动后再申请这些用户体验影响较大的权限，就显的很差劲了。
     * <p>
     * 注1：Android 13开始，新增了通知权限，详情参见：https://mp.weixin.qq.com/s/BQ57pzaffB8vPyGuJtc37w，经
     *     Jack Jiang测试验证，如果不对通知权限进行申请，则所有的Notification，包括前台服务绑定的常驻任务栏Notification，
     *     都将被系统忽略（无权限的效果就是：手机上没通知效果、开发者模式下的控制台也没有任何log或异常信息输出）。
     * 注2：因权限的申请需要在Activiry的上下文下才能完成（这是XXPermissions权限框架决定的），所以后续收到消息等情况下
     *     再申请就搞不定了，因为RBchat中收到消息时的context上下文通常是Application实例，它不是Activity！
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     * @param showSettingIfNever true表示当正在获取已被用户永久拒绝的权限时自动跳到系统权限设置界面，否则不跳转
     * @see #requestPermission_POST_NOTIFICATIONS(Context, Observer, Observer) 
     * @see #requestPermission_STORAGE(Activity, Observer, Observer, boolean)
     * @since 9.0
     */
    public static void requestPermission_STORAGE_$_POST_NOTIFICATIONS(final Activity activity
            , final Observer obsOnGranted, final Observer obsOnDenied, final boolean showSettingIfNever)
    {
        // 本次要动态申请的权限（在Android13及以上版本中，使用READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE无法实现权限申请）
//      final String[] pemmisions = {Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE};
        final String[] pemmisions = {
                /* 图片、视频和存储权限 */
                  Permission.READ_MEDIA_IMAGES
                , Permission.READ_MEDIA_VIDEO
                , Permission.READ_MEDIA_AUDIO
                , Permission.READ_MEDIA_VISUAL_USER_SELECTED

                /* 发送通知权限 */
                , Permission.POST_NOTIFICATIONS

                /* 在Android 14机型上，遇到过FOREGROUND_SERVICE_REMOTE_MESSAGING权限报错而崩溃，以下权限需要申请？需在更多的机型上测试和观察！ */
//              , "android.permission.FOREGROUND_SERVICE"
//              , "android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING"
        };
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , showSettingIfNever
                , pemmisions);
    }

    /**
     * 请求 RECORD_AUDIO 和 CAMERA 权限（此种组合，通常用于视频聊天、短视频录制时）。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     */
    public static void requestPermission_CAMERA_$_RECORD_AUDIO(final Activity activity
            , final Observer obsOnGranted, Observer obsOnDenied)
    {
        // 本次要动态申请的权限
        final String[] pemmisions = {Permission.CAMERA, Permission.RECORD_AUDIO};

        // 如果拒绝或没有获取到权限时的观察者没有设置，则给个默认的（主要用于信息提示）
        if(obsOnDenied ==null)
        {
            obsOnDenied = (o, permissionNamesObj) -> {
                final List<String> permissionNames = (List<String>)permissionNamesObj;
                String message = MessageFormat.format(activity.getResources().getString(R.string.rb_permission_fail_to_cancel), permissionNames);
                Log.w(TAG, "[动态权限onDenied]"+message);
                WidgetUtils.showToastLong(activity, message, WidgetUtils.ToastType.WARN);
            };
        }

        // 开始动态权限申请（用于targetSDKVersion>=23，即Android6.0及以上系统时）
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , true
                , pemmisions);
    }

    /**
     * 请求 CAMERA 权限。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     */
    public static void requestPermission_CARMERA(final Activity activity
            , final Observer obsOnGranted, Observer obsOnDenied)
    {
        // 本次要动态申请的权限
        final String[] pemmisions = {Permission.CAMERA};

        // 如果拒绝或没有获取到权限时的观察者没有设置，则给个默认的（主要用于信息提示）
        if(obsOnDenied == null)
        {
            obsOnDenied = (o, permissionNamesObj) -> {
                final List<String> permissionNames = (List<String>)permissionNamesObj;
                String message = MessageFormat.format(activity.getResources().getString(R.string.rb_permission_fail_to_cancel), permissionNames);
                Log.w(TAG, "[动态权限onDenied]"+message);
                WidgetUtils.showToastLong(activity, message, WidgetUtils.ToastType.WARN);
            };
        }

        // 开始动态权限申请（用于targetSDKVersion>=23，即Android6.0及以上系统时）
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , true
                , pemmisions);
    }

    /**
     * 请求 RECORD_AUDIO 权限。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     */
    public static void requestPermission_RECORD_AUDIO(final Activity activity
            , final Observer obsOnGranted, Observer obsOnDenied)
    {
        // 本次要动态申请的权限
        final String[] pemmisions = {Permission.RECORD_AUDIO};

        // 如果拒绝或没有获取到权限时的观察者没有设置，则给个默认的（主要用于信息提示）
        if(obsOnDenied == null)
        {
            obsOnDenied = (o, permissionNamesObj) -> {
                final List<String> permissionNames = (List<String>) permissionNamesObj;
                String message = MessageFormat.format(activity.getResources().getString(R.string.rb_permission_fail_to_cancel), permissionNames);
                Log.w(TAG, "[动态权限onDenied]" + message);
                WidgetUtils.showToastLong(activity, message, WidgetUtils.ToastType.WARN);
            };
        }

        // 开始动态权限申请（用于targetSDKVersion>=23，即Android6.0及以上系统时）
        PermissionManager.requestPermission(activity
                // 权限获取成功或已经取得此权限时
                , obsOnGranted
                // 用户拒绝或权限获取失败时
                , obsOnDenied
                , true
                , pemmisions);
    }

    /**
     * 请求权限。
     *
     * @param activity 调用时的主类
     * @param obsOnGranted 已同意权限或已拥有此权限时的观察者，不可为null
     * @param obsOnDenied 已拒绝权限或无此权限时的观察者，可为null（为null时将使用默认观察者，主要用于信息提示）
     * @param showSettingIfNever true表示当正在获取已被用户永久拒绝的权限时自动跳到系统权限设置界面，否则不跳转
     * @param permissions 要请求的权限，数组形式
     * @see com.hjq.permissions.Permission
     */
    public static void requestPermission(final Context activity, final Observer obsOnGranted
            , final Observer obsOnDenied, final boolean showSettingIfNever, String... permissions) {
        final List<String> permissionAllNames = PermissionNameConvert.permissionsToNames(activity, Arrays.asList(permissions));

        Log.i(TAG, "[动态权限requestPermission]权限"+ Arrays.toString(permissionAllNames.toArray())+"正在请求中。。。");

        XXPermissions.with(activity)
                // 申请权限
                .permission(permissions)
                .interceptor(new PermissionInterceptor())
                .request(new OnPermissionCallback() {

                    /**
                     * 有权限被同意授予时回调
                     *
                     * @param grantedPermissions 请求成功的权限组
                     * @param all 是否全部授予了
                     */
                    @Override
                    public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                        if (!allGranted) {
                            Log.i(TAG, "[动态权限onGranted]【部分】权限"+ Arrays.toString(permissions.toArray())+"获取成功，本次将忽略且不通知应用层的obsOnGranted！");
                            return;
                        }

                        Log.i(TAG, "[动态权限onGranted]【全部】权限"+ Arrays.toString(permissions.toArray())+"获取成功。");
                        final List<String> permissionNames = PermissionNameConvert.permissionsToNames(activity, permissions);//Permission.transformText(activity, permissions);
                        if (obsOnGranted != null)
                            obsOnGranted.update(null, permissionNames);
                    }

                    /**
                     * 有权限被拒绝授予时回调
                     *
                     * @param permissions 请求失败的权限组
                     * @param doNotAskAgain  是否勾选了不再询问选项
                     */
                    @Override
                    public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                        Log.e(TAG, "[动态权限onDenied]权限"+Arrays.toString(permissions.toArray())+"获取失败！");

                        List<String> permissionNames = PermissionNameConvert.permissionsToNames(activity, permissions);//Permission.transformText(activity, permissions);
                        if(obsOnDenied != null)
                            obsOnDenied.update(null, permissionNames);

                        // 显示系统权限设置
                        if(doNotAskAgain && showSettingIfNever){
                            if(activity instanceof Activity) {
                                showSettingDialog((Activity)activity, permissions);
                            } else {
                                Log.e(TAG, "[动态权限onDenied]权限"+Arrays.toString(permissions.toArray())+"获取失败，且显示系统权限设置对话框也失败了，原因是上下文对象不是Activity！！");
                            }
                        }
                    }
                });
    }

    /**
     * 显示授权对话框
     */
    public static void showSettingDialog(Activity activity, List<String> permissions) {
        new com.eva.android.widget.alert.AlertDialog.Builder(activity)
                .setTitle(R.string.common_permission_alert)
                .setMessage(PermissionNameConvert.getPermissionNames(activity, permissions))
                .setPositiveButton(R.string.rb_permission_setting_btn_go, (dialog, which) -> {
                    dialog.dismiss();
                    XXPermissions.startPermissionActivity(activity, permissions);
                })
                .setNegativeButton(R.string.general_cancel, null)
                .show();
    }
}
